{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/jck/anaconda3/lib/python3.6/site-packages/h5py/__init__.py:36: FutureWarning: Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`.\n",
      "  from ._conv import register_converters as _register_converters\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING:tensorflow:From /home/jck/anaconda3/lib/python3.6/site-packages/tensorflow/contrib/learn/python/learn/datasets/base.py:198: retry (from tensorflow.contrib.learn.python.learn.datasets.base) is deprecated and will be removed in a future version.\n",
      "Instructions for updating:\n",
      "Use the retry module or similar alternatives.\n"
     ]
    }
   ],
   "source": [
    "\"\"\"A very simple MNIST classifier.\n",
    "See extensive documentation at\n",
    "https://www.tensorflow.org/get_started/mnist/beginners\n",
    "\"\"\"\n",
    "from __future__ import absolute_import\n",
    "from __future__ import division\n",
    "from __future__ import print_function\n",
    "\n",
    "import argparse\n",
    "import sys\n",
    "\n",
    "from tensorflow.examples.tutorials.mnist import input_data\n",
    "\n",
    "import tensorflow as tf\n",
    "\n",
    "FLAGS = None\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们在这里调用系统提供的Mnist数据函数为我们读入数据，如果没有下载的话则进行下载。\n",
    "\n",
    "<font color=#ff0000>**这里将data_dir改为适合你的运行环境的目录**</font>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING:tensorflow:From <ipython-input-2-dec1ed799c90>:3: read_data_sets (from tensorflow.contrib.learn.python.learn.datasets.mnist) is deprecated and will be removed in a future version.\n",
      "Instructions for updating:\n",
      "Please use alternatives such as official/mnist/dataset.py from tensorflow/models.\n",
      "WARNING:tensorflow:From /home/jck/anaconda3/lib/python3.6/site-packages/tensorflow/contrib/learn/python/learn/datasets/mnist.py:260: maybe_download (from tensorflow.contrib.learn.python.learn.datasets.base) is deprecated and will be removed in a future version.\n",
      "Instructions for updating:\n",
      "Please write your own downloading logic.\n",
      "WARNING:tensorflow:From /home/jck/anaconda3/lib/python3.6/site-packages/tensorflow/contrib/learn/python/learn/datasets/mnist.py:262: extract_images (from tensorflow.contrib.learn.python.learn.datasets.mnist) is deprecated and will be removed in a future version.\n",
      "Instructions for updating:\n",
      "Please use tf.data to implement this functionality.\n",
      "Extracting wowowo/tensorflow/minst/input_data/train-images-idx3-ubyte.gz\n",
      "WARNING:tensorflow:From /home/jck/anaconda3/lib/python3.6/site-packages/tensorflow/contrib/learn/python/learn/datasets/mnist.py:267: extract_labels (from tensorflow.contrib.learn.python.learn.datasets.mnist) is deprecated and will be removed in a future version.\n",
      "Instructions for updating:\n",
      "Please use tf.data to implement this functionality.\n",
      "Extracting wowowo/tensorflow/minst/input_data/train-labels-idx1-ubyte.gz\n",
      "WARNING:tensorflow:From /home/jck/anaconda3/lib/python3.6/site-packages/tensorflow/contrib/learn/python/learn/datasets/mnist.py:110: dense_to_one_hot (from tensorflow.contrib.learn.python.learn.datasets.mnist) is deprecated and will be removed in a future version.\n",
      "Instructions for updating:\n",
      "Please use tf.one_hot on tensors.\n",
      "Extracting wowowo/tensorflow/minst/input_data/t10k-images-idx3-ubyte.gz\n",
      "Extracting wowowo/tensorflow/minst/input_data/t10k-labels-idx1-ubyte.gz\n",
      "WARNING:tensorflow:From /home/jck/anaconda3/lib/python3.6/site-packages/tensorflow/contrib/learn/python/learn/datasets/mnist.py:290: DataSet.__init__ (from tensorflow.contrib.learn.python.learn.datasets.mnist) is deprecated and will be removed in a future version.\n",
      "Instructions for updating:\n",
      "Please use alternatives such as official/mnist/dataset.py from tensorflow/models.\n"
     ]
    }
   ],
   "source": [
    "# Import data\n",
    "data_dir = 'wowowo/tensorflow/minst/input_data' #需要把下载的数据保存到什么地方\n",
    "mnist = input_data.read_data_sets(data_dir, one_hot=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "一个非常非常简陋的模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create the model\n",
    "x = tf.placeholder(tf.float32, [None, 784])\n",
    "#W = tf.Variable(tf.zeros([784, 10]))\n",
    "#b = tf.Variable(tf.zeros([10]))\n",
    "#y = tf.matmul(x, W) + b\n",
    "keep_prob = tf.placeholder(tf.float32)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "'''\n",
    "w1 = tf.Variable(tf.zeros([784,500]))\n",
    "b1 = tf.Variable(tf.zeros([500]))\n",
    "y1 = tf.nn.tanh(tf.matmul(x,w1) + b1)\n",
    "y1_drop = tf.nn.dropout(y1,keep_prob)\n",
    "'''\n",
    "W1 = tf.Variable(tf.truncated_normal([784, 500], stddev=0.1))  \n",
    "b1 = tf.Variable(tf.zeros([500]) )  \n",
    "y1 = tf.nn.relu(tf.matmul(x, W1) + b1)  \n",
    "y1_drop = tf.nn.dropout(y1,keep_prob)  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "'''\n",
    "w2 = tf.Variable(tf.zeros([500,300]))\n",
    "b2 = tf.Variable(tf.zeros([300]))\n",
    "y2 = tf.nn.tanh(tf.matmul(y1_drop,w2) + b2)\n",
    "y2_drop = tf.nn.dropout(y2,keep_prob)\n",
    "'''\n",
    "W2 = tf.Variable(tf.truncated_normal([500, 300], stddev=0.1))  \n",
    "b2 = tf.Variable(tf.zeros([300]) )  \n",
    "y2 = tf.nn.tanh(tf.matmul(y1_drop, W2) + b2)  \n",
    "y2_drop = tf.nn.dropout(y2,keep_prob)  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "W3 = tf.Variable(tf.truncated_normal([300,200], stddev=0.1))  \n",
    "b3 = tf.Variable(tf.zeros([200]) )  \n",
    "y3 = tf.nn.relu(tf.matmul(y2_drop, W3) + b3)  \n",
    "y3_drop = tf.nn.dropout(y3,keep_prob)  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "W4 = tf.Variable(tf.truncated_normal([200,100], stddev=0.1))  \n",
    "b4 = tf.Variable(tf.zeros([100]) )  \n",
    "y4 = tf.nn.tanh(tf.matmul(y3_drop, W4) + b4)  \n",
    "y4_drop = tf.nn.dropout(y4,keep_prob)  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "'''\n",
    "w3 = tf.Variable(tf.zeros([300,10]))\n",
    "b3 = tf.Variable(tf.zeros([10]))\n",
    "y =tf.nn.softmax(tf.matmul(y2_drop,w3) + b3)\n",
    "'''\n",
    "W5 = tf.Variable(tf.truncated_normal([100, 10], stddev=0.1))  \n",
    "b5 = tf.Variable(tf.zeros([10]) )  \n",
    "#y = tf.matmul(y4, W5) + b5\n",
    "y = tf.matmul(y4_drop, W5) + b5"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "定义我们的ground truth 占位符"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Define loss and optimizer\n",
    "y_ = tf.placeholder(tf.float32, [None, 10])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "接下来我们计算交叉熵，注意这里不要使用注释中的手动计算方式，而是使用系统函数。\n",
    "另一个注意点就是，softmax_cross_entropy_with_logits的logits参数是**未经激活的wx+b**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING:tensorflow:From <ipython-input-10-bf86c3447efc>:11: softmax_cross_entropy_with_logits (from tensorflow.python.ops.nn_ops) is deprecated and will be removed in a future version.\n",
      "Instructions for updating:\n",
      "\n",
      "Future major versions of TensorFlow will allow gradients to flow\n",
      "into the labels input on backprop by default.\n",
      "\n",
      "See tf.nn.softmax_cross_entropy_with_logits_v2.\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# The raw formulation of cross-entropy,\n",
    "#\n",
    "#   tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(tf.nn.softmax(y)),\n",
    "#                                 reduction_indices=[1]))\n",
    "#\n",
    "# can be numerically unstable.\n",
    "#\n",
    "# So here we use tf.nn.softmax_cross_entropy_with_logits on the raw\n",
    "# outputs of 'y', and then average across the batch.\n",
    "cross_entropy = tf.reduce_mean(\n",
    "    tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "生成一个训练step"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "train_step = tf.train.AdamOptimizer(0.00017).minimize(cross_entropy)\n",
    "\n",
    "sess = tf.Session()\n",
    "init_op = tf.global_variables_initializer()\n",
    "sess.run(init_op)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在这里我们仍然调用系统提供的读取数据，为我们取得一个batch。\n",
    "然后我们运行3k个step(5 epochs)，对权重进行优化。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Train\n",
    "for _ in range(3000):\n",
    "  batch_xs, batch_ys = mnist.train.next_batch(100)\n",
    "  sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys,keep_prob:1.0}) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "验证我们模型在测试数据上的准确率"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.9779\n"
     ]
    }
   ],
   "source": [
    "  # Test trained model\n",
    "correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))\n",
    "accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))\n",
    "\n",
    "print(sess.run(accuracy, feed_dict={x: mnist.test.images,\n",
    "                                      y_: mnist.test.labels,keep_prob:1.0}))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "毫无疑问，这个模型是一个非常简陋，性能也不理想的模型。目前只能达到92%左右的准确率。\n",
    "接下来，希望大家利用现有的知识，将这个模型优化至98%以上的准确率。\n",
    "Hint：\n",
    "- 多隐层\n",
    "- 激活函数\n",
    "- 正则化\n",
    "- 初始化\n",
    "- 摸索一下各个超参数\n",
    "  - 隐层神经元数量\n",
    "  - 学习率\n",
    "  - 正则化惩罚因子\n",
    "  - 最好每隔几个step就对loss、accuracy等等进行一次输出，这样才能有根据地进行调整"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "现在已经加了四个隐层，激活函数尝试用了tanh和rulu，然而都没有达到98%正确率以上，当然正则化还没有进行尝试加上，不太懂如何对正则项进行处理。当时进行权重与偏置量初始化时，用的是tf.zeros,然后进行双隐层执行，正确率下降到1%，这是为什末那？查了相关资料，没有得到解答，麻烦您批改作业时帮忙解答下，然后才用tf.truncated_normal进行初始化，试着继续加隐层，然而正确率没有太大的提高，所以放弃加啦，下面采用另外一种方法来进行测试"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "#每个批次的大小  \n",
    "#以矩阵的形式放进去  \n",
    "batch_size = 100  \n",
    "#计算一共有多少个批次  \n",
    "n_batch = mnist.train.num_examples // batch_size  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "#定义三个placeholder  \n",
    "#28 x 28 = 784  \n",
    "x = tf.placeholder(tf.float32, [None, 784])  \n",
    "y = tf.placeholder(tf.float32, [None, 10])  \n",
    "keep_prob = tf.placeholder(tf.float32)  \n",
    "#学习率  \n",
    "lr = tf.Variable(0.001, dtype=tf.float32)  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "#创建一个的神经网络  \n",
    "#输入层784，隐藏层一500，隐藏层二300，输出层10个神经元  \n",
    "#隐藏层  \n",
    "W1 = tf.Variable(tf.truncated_normal([784, 500], stddev=0.1))  \n",
    "b1 = tf.Variable(tf.zeros([500]) + 0.1)  \n",
    "L1 = tf.nn.tanh(tf.matmul(x, W1) + b1)  \n",
    "L1_drop = tf.nn.dropout(L1,keep_prob)  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "W2 = tf.Variable(tf.truncated_normal([500, 300], stddev=0.1))  \n",
    "b2 = tf.Variable(tf.zeros([300]) + 0.1)  \n",
    "L2 = tf.nn.tanh(tf.matmul(L1_drop, W2) + b2)  \n",
    "L2_drop = tf.nn.dropout(L2,keep_prob)  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "W3 = tf.Variable(tf.truncated_normal([300, 10], stddev=0.1))  \n",
    "b3 = tf.Variable(tf.zeros([10]) + 0.1)  \n",
    "prediction = tf.matmul(L2_drop, W3) + b3"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "#交叉熵代价函数  \n",
    "loss = tf.nn.softmax_cross_entropy_with_logits(labels=y, logits=prediction) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "#训练  \n",
    "train_step = tf.train.AdamOptimizer(lr).minimize(loss)  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "#初始化变量  \n",
    "init = tf.global_variables_initializer()  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "#结果存放在一个布尔型列表中  \n",
    "#tf.argmax(y, 1)与tf.argmax(prediction, 1)相同返回True,不同则返回False  \n",
    "#argmax返回一维张量中最大的值所在的位置  \n",
    "correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(prediction, 1))  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "#求准确率  \n",
    "#tf.cast(correct_prediction, tf.float32) 将布尔型转换为浮点型  \n",
    "accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))  \n",
    "  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Iter0, Testing Accuracy0.9632, Learning_rate0.001\n",
      "Iter1, Testing Accuracy0.9725, Learning_rate0.00095\n",
      "Iter2, Testing Accuracy0.9768, Learning_rate0.0009025\n",
      "Iter3, Testing Accuracy0.9767, Learning_rate0.000857375\n",
      "Iter4, Testing Accuracy0.9798, Learning_rate0.00081450626\n",
      "Iter5, Testing Accuracy0.9805, Learning_rate0.0007737809\n",
      "Iter6, Testing Accuracy0.979, Learning_rate0.0007350919\n",
      "Iter7, Testing Accuracy0.981, Learning_rate0.0006983373\n",
      "Iter8, Testing Accuracy0.9814, Learning_rate0.0006634204\n",
      "Iter9, Testing Accuracy0.9821, Learning_rate0.0006302494\n",
      "Iter10, Testing Accuracy0.9813, Learning_rate0.0005987369\n",
      "Iter11, Testing Accuracy0.9772, Learning_rate0.0005688001\n",
      "Iter12, Testing Accuracy0.9809, Learning_rate0.0005403601\n",
      "Iter13, Testing Accuracy0.9817, Learning_rate0.0005133421\n",
      "Iter14, Testing Accuracy0.9818, Learning_rate0.000487675\n",
      "Iter15, Testing Accuracy0.982, Learning_rate0.00046329122\n",
      "Iter16, Testing Accuracy0.982, Learning_rate0.00044012666\n",
      "Iter17, Testing Accuracy0.9822, Learning_rate0.00041812033\n",
      "Iter18, Testing Accuracy0.9824, Learning_rate0.00039721432\n",
      "Iter19, Testing Accuracy0.9819, Learning_rate0.0003773536\n",
      "Iter20, Testing Accuracy0.9823, Learning_rate0.00035848594\n",
      "Iter21, Testing Accuracy0.9826, Learning_rate0.00034056162\n",
      "Iter22, Testing Accuracy0.9826, Learning_rate0.00032353355\n",
      "Iter23, Testing Accuracy0.9824, Learning_rate0.00030735688\n",
      "Iter24, Testing Accuracy0.9823, Learning_rate0.000291989\n",
      "Iter25, Testing Accuracy0.9825, Learning_rate0.00027738957\n",
      "Iter26, Testing Accuracy0.9762, Learning_rate0.0002635201\n",
      "Iter27, Testing Accuracy0.9821, Learning_rate0.00025034408\n",
      "Iter28, Testing Accuracy0.9822, Learning_rate0.00023782688\n",
      "Iter29, Testing Accuracy0.9819, Learning_rate0.00022593554\n",
      "Iter30, Testing Accuracy0.9821, Learning_rate0.00021463877\n",
      "Iter31, Testing Accuracy0.9819, Learning_rate0.00020390682\n",
      "Iter32, Testing Accuracy0.9821, Learning_rate0.00019371149\n",
      "Iter33, Testing Accuracy0.9822, Learning_rate0.0001840259\n",
      "Iter34, Testing Accuracy0.982, Learning_rate0.00017482461\n",
      "Iter35, Testing Accuracy0.9822, Learning_rate0.00016608338\n",
      "Iter36, Testing Accuracy0.9821, Learning_rate0.00015777921\n",
      "Iter37, Testing Accuracy0.9823, Learning_rate0.00014989026\n",
      "Iter38, Testing Accuracy0.9824, Learning_rate0.00014239574\n",
      "Iter39, Testing Accuracy0.9822, Learning_rate0.00013527596\n",
      "Iter40, Testing Accuracy0.9825, Learning_rate0.00012851215\n",
      "Iter41, Testing Accuracy0.9829, Learning_rate0.00012208655\n",
      "Iter42, Testing Accuracy0.9827, Learning_rate0.00011598222\n",
      "Iter43, Testing Accuracy0.9825, Learning_rate0.00011018311\n",
      "Iter44, Testing Accuracy0.9825, Learning_rate0.000104673956\n",
      "Iter45, Testing Accuracy0.9824, Learning_rate9.944026e-05\n",
      "Iter46, Testing Accuracy0.9829, Learning_rate9.446825e-05\n",
      "Iter47, Testing Accuracy0.9823, Learning_rate8.974483e-05\n",
      "Iter48, Testing Accuracy0.9825, Learning_rate8.525759e-05\n",
      "Iter49, Testing Accuracy0.9826, Learning_rate8.099471e-05\n",
      "Iter50, Testing Accuracy0.9822, Learning_rate7.6944976e-05\n"
     ]
    }
   ],
   "source": [
    "with tf.Session() as sess:  \n",
    "    sess.run(init)  \n",
    "    #总共51个周期  \n",
    "    for epoch in range(51):  \n",
    "        #刚开始学习率比较大，后来慢慢变小  \n",
    "        sess.run(tf.assign(lr, 0.001 * (0.95 ** epoch)))  \n",
    "        #总共n_batch个批次  \n",
    "        for batch in range(n_batch):  \n",
    "            #获得一个批次  \n",
    "            batch_xs, batch_ys = mnist.train.next_batch(batch_size)  \n",
    "            sess.run(train_step, feed_dict={x:batch_xs, y:batch_ys, keep_prob:1.0})  \n",
    "          \n",
    "        learning_rate = sess.run(lr)  \n",
    "        #训练完一个周期后测试数据准确率  \n",
    "        acc = sess.run(accuracy, feed_dict={x:mnist.test.images, y:mnist.test.labels, keep_prob:1.0})  \n",
    "          \n",
    "        print(\"Iter\" + str(epoch) + \", Testing Accuracy\" + str(acc)+ \", Learning_rate\" + str(learning_rate))  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "其中采用了tf.train.AdamOptimizer,此函数是Adam优化算法：是一个寻找全局最优点的优化算法，引入了二次方梯度校正，相比于基础SGD算法，不容易陷入局部最优点，速度更快。从打印记过可以看出达到了98%的正确率。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
